home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / console / svgatext.3 / svgatext / SVGATextMode-1.3 / contrib / vgaset / vgaset.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-24  |  22.2 KB  |  768 lines

  1. /* $Id: vgaset.c,v 1.2 1995/01/23 13:58:59 grog Exp $ 
  2.  *
  3.  * $Log: vgaset.c,v $
  4.  * Revision 1.2  1995/01/23  13:58:59  grog
  5.  * Cosmetic mods
  6.  *
  7.  */
  8. /* vgaset.c: set VGA registers for X server.
  9.  * Greg Lehey */
  10. /* Copyright 1992 LEMIS, Schellnhausen 2, 36325 Feldatal, Germany
  11.  * Telephone +49-6637-919123, fax +49-6637-919122
  12.  * This software may be used for noncommercial purposes provided that this copyright notice
  13.  * is not removed. Contact lemis (lemis@lemis.de) for
  14.  * permission to distribute or use for commercial purposes.
  15.  *
  16.  * If you modify this software, please let me know how I blew it - I need this information
  17.  * for people who come to me asking about it.
  18.  */
  19.  
  20. #include <ctype.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sys/fcntl.h>
  25. #include <sys/ioctl.h>
  26. /* This used to be sys/errno.h, but it broke on some System V machines.
  27.  * This seems to work for all - please contact me if you have trouble */
  28. #include <errno.h>
  29. #include <termios.h>
  30. #ifndef __bsdi__
  31. #include "compiler.h"                        /* this should be the x386/common/compiler.h */
  32. #endif
  33. #ifdef HAS_USL_VTS
  34. #include <sys/kd.h>
  35.  
  36. #define CONSOLE "/dev/console"                    /* name of console */
  37. int console;                            /* file descriptor for console */
  38. #endif
  39. #include "svga.h"
  40. #ifdef __GNUC__
  41. #define INLINE inline                        /* inline */
  42. #else
  43. #define INLINE                            /* other compilers don't handle it */
  44. #endif
  45.  
  46. #define MAXEHB    0xf8                        /* highest value of EHB */
  47.  
  48. struct termios prior_term_status;                /* terminfos to reset to on close */
  49. struct termios term_status;                    /* and current values */
  50.  
  51. float dotclock = 0;                        /* dot clock frequency */
  52. char *crtc_reg_name [] =
  53. {
  54.   "HT", "HDE", "SHB", "EHB", "SHR", "EHR", "VT", "OVERFLOW", "PRS",
  55.   "MSL", "CS", "CE", "SAH", "SAL", "CLH", "CLL", "VRS", "EVR", "VDE",
  56.   "OFF", "UL", "VBS", "VBE", "MODE CONTROL", "LC"
  57.     };
  58.  
  59. /* These are the ``real'' values of the registers. They need to be collected from the
  60.  * VGA registers or scattered to them */
  61. struct crtc_regs
  62. {
  63.   short int hde,                        /* horizontal display end */
  64.         shr,                        /* start of horizontal retrace */
  65.         ehr,                        /* end of horizontal retrace */
  66.         shb,                        /* start of horizontal blanking */
  67.         ehb,                        /* end of horizontal blanking */
  68.         ht,                            /* horizontal total */
  69.         vde,                        /* vertical display end */
  70.         vbs,                        /* vertical blanking start */
  71.         vbe,                        /* vertical blanking end */
  72.         vrs,                        /* vertical retrace start */
  73.         evr,                        /* end of vertical retrace */
  74.         vt,                            /* vertical total */
  75.         overflow,                        /* overflow register */
  76.         msl;                        /* and maximum scan line reg */
  77.   }
  78. current_regs,
  79.   saved_regs;
  80.  
  81. int verbose = 0;                        /* set to show more info */
  82. int trident = 0,                        /* set if a TVGA8800 or 8900 */
  83.     tseng = 0;                            /* set if a T3000 or T4000 */
  84.  
  85. int vgaIOBase;
  86.  
  87. #ifdef __bsdi__
  88. static INLINE void
  89.   outb (short port, char val)
  90. {
  91.   __asm__ volatile ("out%B0 %0,%1" : :"a" (val), "d" (port));
  92.   }
  93.  
  94. static INLINE unsigned char
  95.   INB (short port)
  96. {
  97.   unsigned int ret;
  98.   __asm__ volatile ("in%B0 %1,%0" : "=a" (ret) : "d" (port));
  99.   return ret;
  100.   }
  101. #else
  102. static INLINE unsigned char
  103.   INB (short port)
  104. {
  105.   return inb (port) & 0xff;                    /* chop off high-order junk */
  106.   }
  107. #endif
  108.  
  109.  
  110. /* Exit: clean up and exit */
  111. void Exit (int status)
  112. {
  113.   outb (0x3BF, 0x01);                        /* relock ET4000 special */
  114.   outb (vgaIOBase + 8, 0xA0);
  115.   
  116. #ifdef HAS_USL_VTS
  117.   if (ioctl (console, KDDISABIO, 0) < 0)            /* disable port I/O access */
  118.     {
  119.     perror ("Can't disable IO ports");
  120.     exit (status + 1);
  121.     }
  122.   if (ioctl (console, KDDELIO, 0x3BF) < 0)            /* delete port number */
  123.     {
  124.     perror ("Can't delete IO ports");
  125.     exit (status + 1);
  126.     }
  127. #endif
  128.   if (tcsetattr (0, TCSAFLUSH, &prior_term_status))
  129.     {
  130.     printf ("Can't set terminal attributes for stdin: %s\n", strerror (errno));
  131.     Exit (1);
  132.     }
  133.   /* This stuff snarfed from Thomas R|ll's ET4000 driver */
  134.   exit (status);
  135.   }
  136.  
  137. short int getreg (int address, int index)
  138. {
  139.   int val;
  140.   outb (address, index);                    /* set the register number */
  141.   val = INB (address + 1);
  142.   return val;
  143.   }
  144.  
  145. void setreg (int address, int index, short int value)
  146. {
  147.   outb (address, index);                    /* set the register number */
  148.   outb (address + 1, value);
  149.   }
  150.  
  151. void get_crtc_regs ()
  152. {
  153.   current_regs.hde = (getreg (CRTCL_INDEX, HDE) + 1) << 3;
  154.   current_regs.shr = getreg (CRTCL_INDEX, SHR) << 3;
  155.   current_regs.ehr = (getreg (CRTCL_INDEX, EHR) & 0x1f) << 3;
  156.   current_regs.shb = getreg (CRTCL_INDEX, SHB) << 3;
  157.   current_regs.ehb = (getreg (CRTCL_INDEX, EHB) & 0x1f) << 3;
  158.   if (current_regs.ehr <= (current_regs.shr & 0xff))        /* wraparound, */
  159.     current_regs.ehr += 0x100;                    /* adjust */
  160.   current_regs.ehr += (current_regs.shr & ~0xff);        /* this is relative to shr */
  161.   current_regs.ht = (getreg (CRTCL_INDEX, HT) + 5) << 3;
  162.   current_regs.overflow = getreg (CRTCL_INDEX, OVERFLOW);
  163.   switch (current_regs.overflow & 0x21)                /* get the high-order VT stuff */
  164.     {
  165.   case 0:
  166.     current_regs.vt = 0; break;
  167.   case 1:
  168.     current_regs.vt = 256; break;
  169.   case 0x20:
  170.     current_regs.vt = 512; break;
  171.   case 0x21:
  172.     current_regs.vt = 768;
  173.     }
  174.   current_regs.vt += getreg (CRTCL_INDEX, VT) + 2;
  175.   switch (current_regs.overflow & 0x84)                /* get the high-order VRS stuff */
  176.     {
  177.   case 0:
  178.     current_regs.vrs = 0; break;
  179.   case 4:
  180.     current_regs.vrs = 256; break;
  181.   case 0x80:
  182.     current_regs.vrs = 512; break;
  183.   case 0x84:
  184.     current_regs.vrs = 768;
  185.     }
  186.   current_regs.vrs += getreg (CRTCL_INDEX, VRS);
  187.   if ((current_regs.evr = getreg (CRTCL_INDEX, EVR) & 0xf) <= (current_regs.vrs & 0xf)) /* wraparound */
  188.     current_regs.evr += 0x10;
  189.   current_regs.evr += (current_regs.vrs & ~0xf);        /* get base */
  190.   switch (current_regs.overflow & 0x42)                /* get the high-order VDE stuff */
  191.     {
  192.   case 0:
  193.     current_regs.vde = 0; break;
  194.   case 2:
  195.     current_regs.vde = 256; break;
  196.   case 0x40:
  197.     current_regs.vde = 512; break;
  198.   case 0x42:
  199.     current_regs.vde = 768;
  200.     }
  201.   current_regs.vde += getreg (CRTCL_INDEX, VDE) + 1;
  202.   current_regs.vbs = getreg (CRTCL_INDEX, VBS);            /* start of blanking register */
  203.   if (current_regs.overflow & 8)                /* bit 8 of VBS reg */
  204.     current_regs.vbs += 256;                    /* add in */
  205.   if (getreg (CRTCL_INDEX, MSL) & 0x20)                /* bit 9 of VBS reg */
  206.     current_regs.vbs += 512;                    /* add in */
  207.   current_regs.vbe = getreg (CRTCL_INDEX, VBE) & 0x7f;        /* end of blanking register */
  208.   if (current_regs.vbe < (current_regs.vbs & 0x7f))        /* wraparound */
  209.     current_regs.vbe += 128;
  210.   current_regs.vbe += current_regs.vbs & ~0x7f;            /* high-order comes from vbs */
  211.   }
  212.  
  213. void show_crtc_regs ()
  214. {
  215.   int reg;                            /* register number */
  216.   short int val;                        /* value stored in register */
  217.  
  218.   get_crtc_regs ();
  219.   if (dotclock)                            /* show frequencies */
  220.     {
  221.     float horfreq = dotclock / current_regs.ht;
  222.     printf ("Horizontal frequency: %5.0f Hz, vertical frequency: %3.1f Hz\n",
  223.         horfreq,
  224.         horfreq / current_regs.vt );
  225.     printf ("Horizontal sync %5.2f us, vertical sync %5.2f us\n",
  226.         (current_regs.ehr - current_regs.shr) / dotclock * 1000000,
  227.         (current_regs.evr - current_regs.vrs) / horfreq * 1000000 );
  228.     printf ("Horizontal retrace %5.2f us, vertical retrace %5.2f us\n",
  229.         (current_regs.ht - current_regs.hde) / dotclock * 1000000,
  230.         (current_regs.vt - current_regs.vde) / horfreq * 1000000 );
  231.     printf ("\"%dx%d\"\t%d\t%d %d %d %d\t%d %d %d %d\n",
  232.         current_regs.hde,
  233.         current_regs.vde,
  234.         (int) dotclock / 1000000,
  235.         current_regs.hde,
  236.         current_regs.shr,
  237.         current_regs.ehr,
  238.         current_regs.ht,
  239.         current_regs.vde,
  240.         current_regs.vrs,
  241.         current_regs.evr,
  242.         current_regs.vt);                    /* print clocks line */
  243.     }
  244.   else
  245.     printf ("\"%dx%d\"\t**\t%d %d %d %d\t%d %d %d %d\n",
  246.         current_regs.hde,
  247.         current_regs.vde,
  248.         current_regs.hde,
  249.         current_regs.shr,
  250.         current_regs.ehr,
  251.         current_regs.ht,
  252.         current_regs.vde,
  253.         current_regs.vrs,
  254.         current_regs.evr,
  255.         current_regs.vt);                    /* print clocks line */
  256.   if (verbose)
  257.     {
  258.     for (reg = 0; reg < CRTC_REG_COUNT; reg++)
  259.       {
  260.       outb (CRTCL_INDEX, reg);                    /* set the register number */
  261.       val = INB (CRTCL_DATA);
  262.       if (! (reg & 3))
  263.     printf ("\n");                        /* 4 to a line */
  264.       printf ("%s\t%x\t", crtc_reg_name [reg], val);
  265.       }
  266.     printf ("\n");
  267.     }
  268.   }
  269.  
  270. /* set vertical blanking start */
  271. void setvrs ()
  272. {
  273.   current_regs.overflow = getreg (CRTCL_INDEX, OVERFLOW) & ~8;
  274.   if (current_regs.vrs & 0x100)
  275.     current_regs.overflow |= 8;
  276.   setreg (CRTCL_INDEX, OVERFLOW, current_regs.overflow);    /* take care of bit 8 */
  277.   current_regs.msl = getreg (CRTCL_INDEX, MSL) & ~0x20;
  278.   if (current_regs.vrs & 0x200)
  279.     current_regs.msl |= 0x20;
  280.   setreg (CRTCL_INDEX, MSL, current_regs.msl);            /* take care of bit 9 */
  281.   setreg (CRTCL_INDEX, VRS, current_regs.vrs & 0xff);        /* and the first 8 bits */
  282.   }
  283.  
  284. void setevr ()
  285. {
  286.   setreg (CRTCL_INDEX, EVR, (current_regs.evr & 0xf) | (getreg (CRTCL_INDEX, EVR) & ~0xf));
  287.   }
  288.  
  289. void setvt ()
  290. {
  291.   short int myvt = current_regs.vt - 2;                /* adjust for VGA format */
  292.   short int highbits [] = {0, 1, 0x20, 0x21};            /* high-order bits for vt value */
  293.   current_regs.overflow = getreg (CRTCL_INDEX, OVERFLOW) & ~ 0x21;
  294.   current_regs.overflow |= highbits [myvt >> 8];        /* put in high order bits */
  295.   setreg (CRTCL_INDEX, OVERFLOW, current_regs.overflow);
  296.   setreg (CRTCL_INDEX, VT, myvt & 0xff);            /* and low-order 8 bits */
  297.   }
  298.  
  299. void setshr ()
  300. {
  301.   setreg (CRTCL_INDEX, SHR, current_regs.shr >> 3);
  302.   }
  303.  
  304. void setehr ()
  305. {
  306.   setreg (CRTCL_INDEX, EHR, (getreg (CRTCL_INDEX, EHR) & 0xe0) | ((current_regs.ehr >> 3) & 0x1f));
  307.   }
  308.  
  309. void setshb ()
  310. {
  311.   setreg (CRTCL_INDEX, SHB, current_regs.shb >> 3);
  312.   }
  313.  
  314. void setehb ()
  315. {
  316.   setreg (CRTCL_INDEX, EHB, (getreg (CRTCL_INDEX, EHB) & 0xe0) | ((current_regs.ehb >> 3) & 0x1f));
  317.   }
  318.  
  319. void setvbs ()
  320. {
  321.   setreg (CRTCL_INDEX, OVERFLOW, (getreg (CRTCL_INDEX, OVERFLOW) & ~8) + ((current_regs.vbs >> 4) & 8));
  322.   setreg (CRTCL_INDEX, MSL, (getreg (CRTCL_INDEX, MSL) & ~0x10) + ((current_regs.vbs >> 4) & 0x10));
  323.   setreg (CRTCL_INDEX, VBS, current_regs.vbs);
  324.   }
  325.  
  326. void setvbe ()
  327. {
  328.   setreg (CRTCL_INDEX, VBE, (getreg (CRTCL_INDEX, VBE) & 0x80) | (current_regs.vbe & 0x3f));
  329.   }
  330.  
  331. void setht ()
  332. {
  333.   setreg (CRTCL_INDEX, HT, (current_regs.ht >> 3) - 5);
  334.   }
  335.  
  336. void set_interlace ()
  337. {
  338.   if (tseng)
  339.     setreg (CRTCL_INDEX,
  340.         TSENG_OVERFLOW_HIGH,
  341.         (getreg (CRTCL_INDEX, TSENG_OVERFLOW_HIGH) & ~TSENG_INTERLACE) | TSENG_INTERLACE);
  342.   else
  343.     printf ("Sorry, I don't know how to set interlace on this card\n");
  344.   }
  345.  
  346. void reset_interlace ()
  347. {
  348.   if (tseng)
  349.     setreg (CRTCL_INDEX, TSENG_OVERFLOW_HIGH, getreg (CRTCL_INDEX, TSENG_OVERFLOW_HIGH) & ~TSENG_INTERLACE);
  350.   else
  351.     printf ("Sorry, I don't know how to reset interlace on this card\n");
  352.   }
  353.  
  354. /* set_register: prompt for a register name and value */
  355. void set_register ()
  356. {
  357.   int i;
  358.   int reg;                            /* register index in crtc register list */
  359.   int found;                            /* flag for register name found */
  360.   char regname [10];
  361.   while (1)                            /* until we hit EOT */
  362.     {
  363.     i = 0;
  364.     printf ("\bSet register: ");
  365.     while (1)
  366.       {
  367.       regname [i] = getchar ();
  368.       if ((regname [i] == prior_term_status.c_cc [VEOF])    /* EOT, */
  369.       || ((! i) && ((regname [i] == '\r')            /* CR or */
  370.             || (regname [i] == '\n') )) )        /* LF at beginning of line*/
  371.     {
  372.     get_crtc_regs ();                    /* update our memory copy */
  373.     return;
  374.     }
  375.       else if (regname [i] == prior_term_status.c_cc [VERASE]) /* backspace or whatever? */
  376.     {
  377.     if (i > 1)
  378.       {
  379.       printf ("\b\b\b");                    /* erase last character on screen */
  380.       i -= 2;                        /* and in buffer */
  381.       }
  382.     }
  383.       if ((regname [i] < 'A')                    /* should be end of name */
  384.       && i)                            /* and we already have something */
  385.     break;
  386.       if (regname [i] >= 'A')                    /* valid character, */
  387.     regname [i++] &= 0x5f;                    /* convert to upper case */
  388.       }
  389.     regname [i] = '\0';                        /* make a string of it */
  390.     puts ("");                            /* new line */
  391.     for (reg = 0; reg < CRTC_REG_COUNT; reg++)
  392.       {
  393.       if (found = (! strcmp (regname, crtc_reg_name [reg]))) /* found the reg, */
  394.     break;
  395.       }
  396.     if (! found)
  397.       printf ("Can't find register %s\n", regname);
  398.     else                            /* got it, show contents */
  399.       {
  400.       char input [20];
  401.       int val;
  402.       outb (CRTCL_INDEX, reg);                    /* set the register number */
  403.       val = INB (CRTCL_DATA);
  404.       printf ("%s: %x -> 0x", regname, val);
  405.       fgets (input, sizeof (input), stdin);            /* get an input */
  406.       if (sscanf (input, "%x\n", &val))                /* got a new value */
  407.     {
  408.     outb (CRTCL_INDEX, reg);                /* set the register number */
  409.     outb (CRTCL_DATA, val);
  410.     outb (CRTCL_INDEX, reg);                /* set the register number */
  411.     val = INB (CRTCL_DATA);                    /* check it got there */
  412.     printf ("%s: %x\n", crtc_reg_name [reg], val);        /* and show what we have now */
  413.     }
  414.       }
  415.     }
  416.   }
  417.   
  418. void main (int argc, char *argv [])
  419. {
  420.   int i;
  421.   int temp;
  422.   if (tcgetattr (0, &prior_term_status))            /* save current terminfos to restore */
  423.     {
  424.     printf ("Can't get terminal attributes for stdin: %s\n", strerror (errno));
  425.     Exit (1);
  426.     }
  427.   if (tcgetattr (0, &term_status))                /* and get it again to modify */
  428.     {
  429.     printf ("Can't get terminal attributes for stdin: %s\n", strerror (errno));
  430.     Exit (1);
  431.     }
  432.   term_status.c_lflag &= ~ICANON;
  433. #ifdef HAS_USL_VTS                        /* using SVR4 or similar, */
  434.   /* if you set an SVR4 system into raw mode, it will take the VEOF character as the minimum
  435.    * number of characters which will fulfil the read! */
  436.   term_status.c_cc [VMIN] = 1;                    /* complete after one character */
  437.   term_status.c_cc [VTIME] = 0;                    /* and wait forever for a reply */
  438. #endif
  439.   if (tcsetattr (0, TCSAFLUSH, &term_status))
  440.     {
  441.     printf ("Can't set terminal attributes for stdin: %s\n", strerror (errno));
  442.     Exit (1);
  443.     }
  444.   /* This stuff snarfed from Thomas R|ll's ET4000 driver */
  445. #ifdef HAS_USL_VTS
  446.   if ((console = open (CONSOLE, O_RDWR | O_NDELAY)) < 0)    /* can't open console? */
  447.     {
  448.     perror ("Can't open /dev/console");
  449.     exit (1);
  450.     }
  451.   if (ioctl (console, KDADDIO, 0x3BF) < 0)            /* add port to list */
  452.     {
  453.     perror ("Can't add I/O port 0x3bf");
  454.     exit (1);
  455.     }
  456.   if (ioctl (console, KDENABIO, 0) < 0)
  457.     {
  458.     perror ("Can't enable port I/O");
  459.     exit (1);
  460.     }
  461. #endif
  462.   
  463.   vgaIOBase = (INB (0x3CC) & 0x01) ? 0x3D0 : 0x3B0;
  464.   outb (0x3BF, 0x03);                        /* unlock ET4000 special */
  465.   outb (vgaIOBase + 8, 0xA0);
  466.   outb (CRTCL_INDEX, EVR);                    /* End Vertical Retrace: chop off high-order stuff */
  467.   temp = INB (CRTCL_DATA);
  468.   outb (CRTCL_DATA, temp & 0x7F);
  469.   get_crtc_regs ();                        /* save current values */
  470.   memcpy (&saved_regs, ¤t_regs, sizeof (struct crtc_regs));
  471.   /* now try to work out what kind of card we have */
  472.   i = getreg (0x3c4, 0);                    /* get mode control #1 */
  473.   setreg (0x3c4, 0, 0);
  474.   if (getreg (0x3c4, 0) == 2)                    /* inverted bit 1 */
  475.     trident = 1;
  476.   setreg (0x3c4, 0, i);                        /* restore old contents */
  477.   for (i = 1; i < argc; i++)
  478.     {
  479.     if (*argv [i] == '-')                    /* flag */
  480.       {
  481.       switch (argv [i] [1])
  482.     {
  483.       case 'd':
  484.     if ((dotclock = atof (&argv [i] [2])) < 200)        /* get it */
  485.       dotclock *= 1000000;                    /* convert to MHz */
  486.     break;
  487.       case 'i':
  488.     reset_interlace ();
  489.     break;
  490.       case 'I':
  491.     set_interlace ();
  492.     break;
  493.       case 's':                            /* set the registers first */
  494.     if (argc < (i + 7))                    /* not enough args */
  495.       {
  496.       puts ("Not enough arguments to -s option");
  497.       Exit (1);
  498.       }
  499.     current_regs.shr = atoi (argv [++i]);            /* get the values to set the regs to */
  500.     current_regs.ehr = atoi (argv [++i]);
  501.     current_regs.ht = atoi (argv [++i]);
  502.     setshr ();
  503.     setehr ();
  504.     setht ();
  505.     current_regs.vrs = atoi (argv [++i]);
  506.     current_regs.evr = atoi (argv [++i]);
  507.     current_regs.vt = atoi (argv [++i]);
  508.     setvrs ();
  509.     setevr ();
  510.     setvt ();
  511.     break;
  512.       case 'v':
  513.     verbose = 1;
  514.     break;
  515.     }
  516.       }
  517.     else
  518.       {
  519.       printf ("Usage:\n%s [-ddot clock] [-s shr ehr ht vrs evr vt] [-v]\n", argv [0]);
  520.       Exit (1);
  521.       }
  522.     }
  523.   if (trident)
  524.     {
  525.     if (verbose)
  526.       printf ("Looks like a Trident TVGA\n");
  527.     }
  528.   else                                /* see if it's a Tseng */
  529.     {
  530.     /* Tseng identification sequence as per Ferraro, "Programmer's guide to the EGA and VGA cards",
  531.      * page 936. The only problem with this code is that it does not work (just blanks the screen). */
  532. #ifdef FUNCTIONAL
  533.     outb (0x3bf, 3);
  534.     if (INB (0x3cc) == 0xd1)
  535.       outb (0x3d8, 0xa0);
  536.     else
  537.       outb (0x3b8, 0xa0);
  538.     INB (0x3da);                        /*    set attribute index */
  539.     outb (0x3c0, 0x16);
  540.     i = INB (0x3c1);                        /* get ATC misc register */
  541.     INB (0x3da);
  542.     outb (0x3c0, 0x16);
  543.     outb (0x3c0, i ^ 0x10);                    /* set ATC misc register */
  544.     INB (0x3da);
  545.     outb (0x3c0, 0x16);
  546.     tseng = INB (0x3c1) == (i ^ 0x10);                /* if it stayed that way, it's a Tseng */
  547.     INB (0x3da);
  548.     outb (0x3c0, 0x16);
  549.     outb (0x3c0, i);
  550. #else                                /* fudge it */
  551.     tseng = 1;
  552. #endif
  553.     if (tseng && verbose)
  554.       puts ("Looks like a Tseng chip");         
  555.     }
  556.   show_crtc_regs ();                        /* first time round */
  557.   while (1)
  558.     {
  559.     char c = getchar ();
  560.     if (c == prior_term_status.c_cc [VEOF])            /* EOT char, */
  561.       Exit (0);                            /* stop */
  562.     switch (c)
  563.       {
  564.     case 'i':
  565.       puts ("nterlace off");
  566.       reset_interlace ();
  567.       break;
  568.     case 'I':
  569.       puts ("nterlace on");
  570.       set_interlace ();
  571.       break;
  572.     case 'l':                            /* decrease left */
  573.       puts ("eft margin decrease");
  574.       if (current_regs.ht > current_regs.ehr)
  575.     {
  576.     current_regs.ht -= 8;
  577.     setht ();
  578.     }
  579.       else
  580.     printf ("Can't reduce left margin further\n\007");
  581.       break;
  582.     case 'L':                            /* increase left */
  583.       puts ("eft margin increase");
  584.       if (current_regs.ht < 2048)                /* space to increase */
  585.     {
  586.     current_regs.ht += 8;
  587.     setht ();
  588.     }
  589.       else
  590.     printf ("Can't increase horizontal total further\007\n");
  591.       break;
  592.     case 'r':                            /* decrease right */
  593.       puts ("ight margin decrease");
  594.       if (current_regs.shr > current_regs.hde)            /* can decrease */
  595.     {
  596.     current_regs.shr -= 8;
  597.     current_regs.ehr -= 8;
  598.     current_regs.ht -= 8;
  599.     setshr ();
  600.     setehr ();
  601.     setht ();
  602.     /* blanking is not configured explicitly, but if we don't track our sync we're liable
  603.      * to end up blanking parts of the image */
  604.     current_regs.shb -= 8;
  605.     if (current_regs.ehb)                    /* ehb < 0? */
  606.       current_regs.ehb -= 8;
  607.     setshb ();
  608.     setehb ();
  609.     }
  610.       else
  611.     printf ("Can't decrease right margin further\007\n");
  612.       break;
  613.     case 'R':                            /* increase right */
  614.       puts ("ight margin increase");
  615.       if (current_regs.ht < 2048)                /* space to increase */
  616.     {
  617.     current_regs.ht += 8;
  618.     current_regs.ehr += 8;
  619.     current_regs.shr += 8;
  620.     setshr ();
  621.     setehr ();
  622.     setht ();
  623.     current_regs.shb += 8;
  624.     if (current_regs.ehb > MAXEHB)                /* ehb < max? */
  625.       current_regs.ehb += 8;
  626.     setshb ();
  627.     setehb ();
  628.     }
  629.       else
  630.     printf ("Can't increase right margin further\007\n");
  631.       break;
  632.     case 'h':                            /* decrease horizontal sync */
  633.       puts ("orizontal sync decrease");
  634.       if (current_regs.ehr > (current_regs.shr + 8))        /* can decrease */
  635.     {
  636.     current_regs.ehr -= 8;
  637.     setehr ();
  638.     if (current_regs.ehb)                    /* ehb < 0? */
  639.       current_regs.ehb -= 8;
  640.     setehb ();
  641.     }
  642.       else
  643.     printf ("Can't decrease horizontal sync further\007\n");
  644.       break;
  645.     case 'H':                            /* increase horizontal sync */
  646.       puts ("orizontal sync increase");
  647.       if (current_regs.ehr < current_regs.ht)            /* space to increase */
  648.     {
  649.     current_regs.ehr += 8;
  650.     setehr ();
  651.     if (current_regs.ehb > MAXEHB)                /* ehb < max? */
  652.       current_regs.ehb += 8;
  653.     setehb ();
  654.     }
  655.       else
  656.     printf ("Can't increase horizontal sync further\007\n");
  657.       break;
  658. /* Vertical operations */
  659.     case 't':                            /* decrease top */
  660.       puts ("op margin decrease");
  661.       if (current_regs.vt > current_regs.evr)
  662.     {
  663.     current_regs.vt -= 1;
  664.     setvt ();
  665.     }
  666.       else
  667.     printf ("Can't reduce top margin further\n\007");
  668.       break;
  669.     case 'T':                            /* increase top */
  670.       puts ("op margin increase");
  671.       if (current_regs.vt < 1024)                /* space to increase */
  672.     {
  673.     current_regs.vt += 1;
  674.     setvt ();
  675.     }
  676.       else
  677.     printf ("Can't increase vertical total further\007\n");
  678.       break;
  679.     case 'b':                            /* decrease bottom */
  680.       puts ("ottom margin decrease");
  681.       if (current_regs.vrs > current_regs.vde)            /* can decrease */
  682.     {
  683.     current_regs.vrs -= 1;
  684.     current_regs.evr -= 1;
  685.     current_regs.vt -= 1;
  686.     setvrs ();
  687.     setevr ();
  688.     setvt ();
  689.     current_regs.vbs -= 1;
  690.     current_regs.vbe -= 1;
  691.     setvbs ();
  692.     setvbe ();
  693.     }
  694.       else
  695.     printf ("Can't decrease bottom margin further\007\n");
  696.       break;
  697.     case 'B':                            /* increase bottom */
  698.       puts ("ottom margin increase");
  699.       if (current_regs.vt < 1024)                /* space to increase */
  700.     {
  701.     current_regs.vt += 1;
  702.     current_regs.evr += 1;
  703.     current_regs.vrs += 1;
  704.     setvrs ();
  705.     setevr ();
  706.     setvt ();
  707.     current_regs.vbs += 1;
  708.     current_regs.vbe += 1;
  709.     setvbs ();
  710.     setvbe ();
  711.     }
  712.       else
  713.     printf ("Can't increase bottom margin further\007\n");
  714.       break;
  715.     case 'v':                            /* decrease vertical sync */
  716.       puts ("ertical sync decrease");
  717.       if (current_regs.evr > current_regs.vrs)            /* can decrease */
  718.     {
  719.     current_regs.evr -= 1;
  720.     setevr ();
  721.     current_regs.vbe -= 1;
  722.     setvbe ();
  723.     }
  724.       else
  725.     printf ("Can't decrease vertical sync further\007\n");
  726.       break;
  727.     case 'V':                            /* increase vertical sync */
  728.       puts ("ertical sync increase");
  729.       if (current_regs.evr < current_regs.vt)            /* space to increase */
  730.     {
  731.     current_regs.evr += 1;
  732.     setevr ();
  733.     current_regs.vbe += 1;
  734.     setvbe ();
  735.     }
  736.       else
  737.     printf ("Can't increase vertical sync further\007\n");
  738.       break;
  739.     case '\n':                            /* do nothing */
  740.       break;
  741.     case '?':                            /* reset saved values */
  742.       puts ("\bReset to initial values");
  743.       memcpy (¤t_regs, &saved_regs, sizeof (struct crtc_regs));
  744.       setvrs ();
  745.       setevr ();
  746.       setvt ();
  747.       setshr ();
  748.       setehr ();
  749.       setht ();
  750.       setshb ();
  751.       setehb ();
  752.       setvbs ();
  753.       setvbe ();
  754.       break;
  755.     case '=':                            /* set register explicitly */
  756.       set_register ();
  757.       break;
  758.     case 'q':
  759.       puts ("uit");
  760.       Exit (0);
  761.       /* NOTREACHED */
  762.     default:
  763.       printf ("Invalid command\007\n");
  764.       }
  765.     show_crtc_regs ();
  766.     }
  767.   }
  768.